/*
 * Copyright (c) 1999 2000
 * Information Promotion Agency of Japan (IPA),
 * Fujitsu Limited, and 
 * Matsuoka Laboratory, Tokyo Institute of Technology
 * All rights reserved.
 *
 * Redistribution and non-commercial use in source and binary forms, 
 * with or without modification, are permitted provided that the following 
 * conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. All advertising materials mentioning features or use of this 
 *    software must display the following acknowledgement:
 *
 *    This product includes software developed by Information Promotion Agency
 *    of Japan, Fujitsu Limited, and Matsuoka Laboratory, 
 *    Tokyo Institute of Technology.
 *
 * 4. The names "Information Promotion Agency", "Fujitsu Limited",
 *    "Matsuoka Laboratory", or "Tokyo Institute of Technology" should
 *    not be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 5. Any use of the source code or the binary in a commercial product, 
 *    whether may it be the origial representation or in some modified form,
 *    is not permitted without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Revision: 1.88 $
 * $Date: 2000/03/15 04:37:59 $
 * $Author: kouya $
 *
 * Runtime routines for JIT compiled code
 */

#include <signal.h>
#include <sys/types.h>
#include "OpenJIT.h"

static ClassClass *classJavaLangObject;

#if JDK_VERSION >= 12
static sys_mon_t *_linkclass_lock;
#endif

#ifdef HAVE_ASM_SIGCONTEXT_H
#include <asm/sigcontext.h>
#endif
#ifdef HAVE_ASM_UCONTEXT_H
#include <asm/ucontext.h>
#endif

/* set to frame->returnpc and distinguish JVM frame or JIT frame */
#define COMPILED_FRAME_TAG	((unsigned char *)-1)

// #undef CODE_PATCH /* XXX mdw */

#ifdef CODE_PATCH
#define INLINE_INVOKE_VIRTUAL
#endif

/*
 * for long(64bit) operation
 */

typedef union {
    int64_t J;
    struct {
	unsigned lo,hi;
    } I;
} Junion;

typedef union {
    double D;
    struct {
	float hi,lo;
    } F;
} Dunion;

static void handle_exception (ExecEnv *) __attribute__ ((noreturn));
static void OpenJIT_Error(char *, char *) __attribute__ ((noreturn));

/* calling these functions don't require to pop arguments */
static void OpenJIT_getstatic(int index) __attribute__ ((stdcall, regparm(1)));
static int OpenJIT_resolveStaticField(int index) __attribute__ ((stdcall, regparm(1)));
static int OpenJIT_resolveField(int index) __attribute__ ((stdcall, regparm(1)));
static int OpenJIT_resolveString(int index) __attribute__ ((stdcall, regparm(1)));

static HObject *OpenJIT_new_quick(ClassClass *cb) __attribute__ ((stdcall));
static HObject *OpenJIT_new(int index) __attribute__ ((stdcall));
static HArrayOfObject *OpenJIT_anewarray_quick(ClassClass *array_cb, int size) __attribute__ ((stdcall));
static HArrayOfObject *OpenJIT_anewarray(int index, int size) __attribute__ ((stdcall));
static void OpenJIT_check_aastore(HObject *obj, HArrayOfObject *arr) __attribute__ ((stdcall));
static void OpenJIT_checkcast_quick(ClassClass *cb, JHandle *h) __attribute__ ((stdcall));
static void OpenJIT_checkcast(int index, JHandle *h) __attribute__ ((stdcall));
static bool_t OpenJIT_instanceof_quick(ClassClass *cb, JHandle *h) __attribute__ ((stdcall));
static bool_t OpenJIT_instanceof(int index, JHandle *h) __attribute__ ((stdcall));

#ifdef ATTRIB_BEFORE_FUNC
#define ATTRIBUTE(A) __attribute__(A)
#else
#define ATTRIBUTE(A)
#endif

#define ASM __asm__ volatile

#ifdef SYM_UNDERSCORE
__asm__("
_OpenJIT_f2i = OpenJIT_f2i
_OpenJIT_d2i = OpenJIT_f2i
_OpenJIT_f2l = OpenJIT_f2l
_OpenJIT_d2l = OpenJIT_d2l
_OpenJIT_invokeinterface_quick = OpenJIT_invokeinterface_quick
_throw_internal = throw_internal
");
#define ASM_TEXT_LABEL(X) __asm__(".text\n_" #X ":\n");
#else
#define ASM_TEXT_LABEL(X) __asm__(".text\n" #X ":\n");
#endif

#define GET_CALLER_MB(X) \
    do {					\
	struct methodblock **p;			\
	ASM("movl 0(%%ebp),%0" : "=r"(p) :);	\
	X = p[2];				\
    } while(0)

/* This macro save current executing method on Java Frame. */
/* Some native method(ex. fillInStackTrace) refer it. */
/* And when exception occurs, it would be refered. */
#define SET_COMPILED_FRAME(EE, MB)				\
    do {							\
	struct javaframe *frame = EE->current_frame;		\
	frame->current_method = MB;				\
	ASM("movl %%ebp,%0" : "=m"(frame->vars) :);		\
    } while(0)

#define JUMP_IN_COMPILED_CODE(MB)				\
    do {							\
	ASM("leave;jmp *%0" : : "m" (mb->CompiledCode));	\
	/* never comes here */					\
    } while(0)

#define RESOLVE_NATIVE_OR_COMPILE(MB)				\
    if (!ENTRY_IS_FIXED(MB)) {					\
        if (MB->fb.access & ACC_NATIVE) {			\
	    OpenJIT_resolveNativeMethod(MB);			\
	} else if (MB->fb.access & ACC_ABSTRACT) {		\
	    char buf[256];					\
	    jio_snprintf(buf, sizeof(buf), 			\
			 "%s.%s",				\
			 cbName(MB->fb.clazz),			\
			 MB->fb.name);				\
	    OpenJIT_Error(JAVAPKG "AbstractMethodError", buf);	\
	} else if (!COMPILE_ON_THE_WAY(MB)) {			\
	    OpenJIT_compile(EE(), MB);				\
	}							\
    }

#ifdef CODE_PATCH
#define PATCH_CODE_FIELD_ACCESS(ADDR_OR_OFFSET)				\
    do {								\
	volatile unsigned char *caller_pc;				\
	ASM("movl 0x4(%%ebp),%0" : "=r"(caller_pc));			\
	/* back to call inst */						\
	caller_pc -= 5;							\
	/* At first, change "call" to "int $0x3" */			\
	caller_pc[0] = 0xcc;						\
	/* store offset	*/						\
	*(uint32_t *)(caller_pc+1) = (uint32_t)(ADDR_OR_OFFSET);	\
	/* modify "call" to "mov 0xXXXX,%eax" */			\
	caller_pc[0] = 0xb8;						\
    } while(0)

#define PATCH_CODE_CALL(FUNC)						     \
    do {								     \
	volatile unsigned char *caller_pc;				     \
	ASM("movl 0x4(%%ebp),%0" : "=r"(caller_pc));			     \
	/* back to call inst */						     \
	caller_pc -= 5;							     \
	/* At first, change "call" to "int $0x3" */			     \
	caller_pc[0] = 0xcc;						     \
	/* store offset	*/						     \
	*(uint32_t *)(caller_pc+1) = (uint32_t)(FUNC)-(uint32_t)caller_pc-5; \
	/* resume "call" */						     \
	caller_pc[0] = 0xe8;						     \
    } while(0)

#define PATCH_CODE_PUSH_AND_CALL(FUNC, ARG0)				      \
    do {								      \
	volatile unsigned char *caller_pc;				      \
	ASM("movl 0x4(%%ebp),%0" : "=r"(caller_pc));			      \
	/* back to push arg0 inst */					      \
	caller_pc -= 10;						      \
	/* At first, change "push to "int $0x3" */			      \
	caller_pc[0] = 0xcc;						      \
	*(uint32_t *)(caller_pc+1) = (uint32_t)ARG0;			      \
	/* store offset	*/						      \
	*(uint32_t *)(caller_pc+6) = (uint32_t)(FUNC)-(uint32_t)caller_pc-10; \
	/* resume "push" */						      \
	caller_pc[0] = 0x68;						      \
    } while(0)
#else
#define PATCH_CODE_FIELD_ACCESS(ADDR_OR_OFFSET)
#define PATCH_CODE_CALL(FUNC)
#define PATCH_CODE_PUSH_AND_CALL(FUNC, ARG0)
#endif

#define CHECK_PATCHING_CODE()

#if JDK_VERSION < 12
#define RESOLVE_CLASS_CONST(CP, INDEX, TYPE, MB)			     \
    do {								     \
	unsigned char *type_table = CP[CONSTANT_POOL_TYPE_TABLE_INDEX].type; \
	if (!CONSTANT_POOL_TYPE_TABLE_IS_RESOLVED(type_table, INDEX)) {	     \
	    ExecEnv *ee = EE();						     \
	    SET_COMPILED_FRAME(ee, MB);					     \
	    ResolveClassConstant(constant_pool, INDEX, ee, 1 << TYPE);	     \
	    if (exceptionOccurred(ee)) {				     \
		handle_exception(ee);					     \
	    }								     \
	}								     \
    } while(0)
#else /* JDK_VERSION >= 12 */
#define RESOLVE_CLASS_CONST(CP, INDEX, TYPE, MB)			     \
    do {								     \
	ExecEnv *ee;							     \
	unsigned char *type_table = CP[CONSTANT_POOL_TYPE_TABLE_INDEX].type; \
	if (!CONSTANT_POOL_TYPE_TABLE_IS_RESOLVED(type_table, INDEX)) {	     \
	    ee = EE();							     \
	    SET_COMPILED_FRAME(ee, MB);					     \
	    ResolveClassConstant(CP, INDEX, ee, 1 << TYPE);		     \
	    if (exceptionOccurred(ee)) {				     \
		handle_exception(ee);					     \
	    }								     \
	} else if (TYPE == CONSTANT_Methodref) {			     \
	    ee = EE();							     \
	    if (!CCIs(CP[INDEX].mb->fb.clazz, Initialized)) {		     \
		if (CP[INDEX].mb->fb.access & ACC_STATIC) {		     \
		    InitClass(CP[INDEX].mb->fb.clazz);			     \
		}							     \
	    }								     \
	    if (exceptionOccurred(ee)) {				     \
		handle_exception(ee);					     \
	    }								     \
	} else if (TYPE == CONSTANT_Fieldref) {				     \
	    ee = EE();							     \
	    if (!CCIs(CP[INDEX].fb->clazz, Initialized)) {		     \
		if (CP[INDEX].fb->access & ACC_STATIC) {		     \
		    InitClass(CP[INDEX].fb->clazz);			     \
		}							     \
	    }								     \
	    if (exceptionOccurred(ee)) {				     \
		handle_exception(ee);					     \
	    }								     \
	}								     \
    } while(0)
#endif

#define RESOLVE_CLASS_CLASS(INDEX, QUICKFUNC, CLASS)			      \
    do {								      \
	struct methodblock *caller_mb;					      \
	cp_item_type *constant_pool;					      \
	GET_CALLER_MB(caller_mb);					      \
	constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));	      \
	RESOLVE_CLASS_CONST(constant_pool, INDEX, CONSTANT_Class, caller_mb); \
	CLASS = constant_pool[INDEX].p;					      \
	PATCH_CODE_PUSH_AND_CALL(QUICKFUNC, CLASS);			      \
    } while(0)


static char *
slash2dot(char *src, char *dst, int size) {
    char *buf = dst;
    while(--size > 0) {
	char c = *src;
	if (c == '\0') break;
	*dst = (c == '/') ? '.' : c;
	src++;
	dst++;
    }
    *dst = '\0';
    return buf;
}

ASM_TEXT_LABEL(skip_err_beg);

static bool_t
makeCompiledFrame(ExecEnv *ee, struct methodblock *mb)
{
    int         nlocals = mb->nlocals + 1; /* maximum number of locals */
    JavaFrame   *old_frame = ee->current_frame; /* previous frame */
    stack_item  *optop = old_frame->optop; /* Points to just before args */
    JavaStack   *stack = old_frame->javastack; /* Current stack pointer */
    JavaFrame   *frame = (JavaFrame *)(optop + nlocals); /* tentative  */

    if (frame->ostack + mb->maxstack >= stack->end_data) {
#if JDK_VERSION < 12
	stack_item *copied_optop;
	int args_size;

	if (stack->next != 0) {
	    stack = stack->next;
	} else {
	    if (stack->stack_so_far +
		JAVASTACK_CHUNK_SIZE*sizeof(stack->data[0]) > JavaStackSize) {
		SignalError(ee, JAVAPKG "StackOverflowError", 0);
		return FALSE;
	    }
	    stack = CreateNewJavaStack(ee, stack);
	    if (stack == 0) {
		SignalError(ee, JAVAPKG "OutOfMemoryError", 0);
		return FALSE;
	    } 
	}
	frame = (JavaFrame *)(stack->data + nlocals);
	if (frame->ostack + mb->maxstack >= stack->end_data) {
	    /* Can't represent valid frame in a Java stack chunk */
	    SignalError(ee, JAVAPKG "InternalError", 0);
	    return FALSE;
	}
	args_size = mb->args_size;
	for (copied_optop = stack->data; --args_size >= 0 ; ) {
	    copied_optop[args_size] = optop[args_size];
	}
	optop = copied_optop;
#else
	if (!ExpandJavaStack(ee, &stack, &frame, NULL, mb->args_size, 0, 0)) {
	  return FALSE;
	}
#endif
    }
    frame->javastack = stack;
    frame->prev = old_frame;
    frame->vars = 0;
    frame->optop = frame->ostack;
    frame->current_method = mb; 
    frame->constant_pool = NULL;
    frame->returnpc = COMPILED_FRAME_TAG;
    frame->monitor = 0;
    ee->current_frame = frame;
    return TRUE;
}

#define INVOKE_COMPILED_CODE(RET, TYPE)		\
    if (!makeCompiledFrame(ee, mb))		\
	return FALSE;				\
    {						\
	int i;					\
	for (i = mb->args_size-1; i >= 0; i--)	\
	    ASM("pushl %0" : : "m"(optop[i].i));\
    }						\
    ASM("pushl %0" : : "m"(mb));		\
    RET ({ASM("" : : : "%ebx","%esi","%edi");	\
	 (*(TYPE (*) ())mb->CompiledCode)();})

ASM_TEXT_LABEL(invokeCC_beg);

static bool_t
invokeCompiledCodeV(JHandle *o, struct methodblock *mb, int args_size, ExecEnv *ee)
{
    JavaFrame *old_frame = ee->current_frame;
    stack_item *optop = old_frame->optop;

    INVOKE_COMPILED_CODE(;, void);
    ee->current_frame = old_frame;
    return !(exceptionOccurred(ee));
}

static bool_t
invokeCompiledCodeI(JHandle *o, struct methodblock *mb, int args_size, ExecEnv *ee)
{
    JavaFrame *old_frame = ee->current_frame;
    stack_item *optop = old_frame->optop;

    INVOKE_COMPILED_CODE(optop[0].i = , int);
    ee->current_frame = old_frame;
    old_frame->optop++;
    return !(exceptionOccurred(ee));
}

static bool_t
invokeCompiledCodeJ(JHandle *o, struct methodblock *mb, int args_size, ExecEnv *ee)
{
    JavaFrame *old_frame = ee->current_frame;
    stack_item *optop = old_frame->optop;
    Junion retval;

    INVOKE_COMPILED_CODE(retval.J = , int64_t);
    optop[0].i = retval.I.lo;
    optop[1].i = retval.I.hi;
    ee->current_frame = old_frame;
    old_frame->optop += 2;
    return !(exceptionOccurred(ee));
}

static bool_t
invokeCompiledCodeF(JHandle *o, struct methodblock *mb, int args_size, ExecEnv *ee)
{
    JavaFrame *old_frame = ee->current_frame;
    stack_item *optop = old_frame->optop;

    INVOKE_COMPILED_CODE(optop[0].f = , float);
    ee->current_frame = old_frame;
    old_frame->optop++;
    return !(exceptionOccurred(ee));
}

static bool_t
invokeCompiledCodeD(JHandle *o, struct methodblock *mb, int args_size, ExecEnv *ee)
{
    JavaFrame *old_frame = ee->current_frame;
    stack_item *optop = old_frame->optop;
    Dunion retval;

    INVOKE_COMPILED_CODE(retval.D = , double);
    optop[0].f = retval.F.hi;
    optop[1].f = retval.F.lo;
    ee->current_frame = old_frame;
    old_frame->optop += 2;
    return !(exceptionOccurred(ee));
}

ASM_TEXT_LABEL(invokeCC_end);

void *
invokeCompiledCodeTable[] = {
    invokeCompiledCodeV,
    invokeCompiledCodeI,
    invokeCompiledCodeJ,
    invokeCompiledCodeF,
    invokeCompiledCodeD,
};


/* start of function to skip stack frame for exceptions */
ASM_TEXT_LABEL(skip_ex_beg);

#define DISPATCH_NATIVE(OPTOP)						\
    ExecEnv *ee;							\
    struct methodblock *caller_mb;					\
									\
    GET_CALLER_MB(caller_mb);						\
    ee = EE();								\
    SET_COMPILED_FRAME(ee, caller_mb);					\
    OPTOP = (*(stack_item * (*) (stack_item*, ExecEnv*)) mb->code)	\
		((stack_item *)(&arg0), ee);				\
    if (exceptionOccurred(ee))						\
	handle_exception(ee);

static void
dispatchNormNativeV(struct methodblock *mb, void *arg0, ...) 
{
    stack_item *optop;
    DISPATCH_NATIVE(optop);
}

static int
dispatchNormNativeI(struct methodblock *mb, void *arg0, ...) 
{
    stack_item *optop;
    DISPATCH_NATIVE(optop);
    return optop[-1].i;
}

static int64_t
dispatchNormNativeJ(struct methodblock *mb, void *arg0, ...) 
{
    stack_item *optop;
    Junion ret;

    DISPATCH_NATIVE(optop);
    ret.I.lo = optop[-2].i;
    ret.I.hi = optop[-1].i;
    return ret.J;
}

static float
dispatchNormNativeF(struct methodblock *mb, void *arg0, ...) 
{
    stack_item *optop;

    DISPATCH_NATIVE(optop);
    return optop[-1].f;
}

static double
dispatchNormNativeD(struct methodblock *mb, void *arg0, ...) 
{
    stack_item *optop;
    Dunion ret;

    DISPATCH_NATIVE(optop);
    ret.F.hi = optop[-2].f;
    ret.F.lo = optop[-1].f;
    return ret.D;
}

static void *
dispatchNormNativeTable[] = {
    dispatchNormNativeV,
    dispatchNormNativeI,
    dispatchNormNativeJ,
    dispatchNormNativeF,
    dispatchNormNativeD,
};

/* Special case. Native method stub routine breaks a stack operand. */
static int64_t
dispatchNormNativeJ_noarg(struct methodblock *mb) 
{
    stack_item arg0[2];
    stack_item *optop;
    Junion ret;

    DISPATCH_NATIVE(optop);
    ret.I.lo = optop[-2].i;
    ret.I.hi = optop[-1].i;
    return ret.J;
}

/* Special case. Native method stub routine breaks a stack operand. */
static double
dispatchNormNativeD_noarg(struct methodblock *mb) 
{
    stack_item arg0[2];
    stack_item *optop;
    Dunion ret;

    DISPATCH_NATIVE(optop);
    ret.F.hi = optop[-2].f;
    ret.F.lo = optop[-1].f;
    return ret.D;
}

static void
dispatchSyncNative(struct methodblock *mb, void *arg0, ...) 
{
    stack_item *optop;
    ExecEnv *ee;
    struct methodblock *caller_mb;
    unsigned int key;

    GET_CALLER_MB(caller_mb);
    ee = EE();
    SET_COMPILED_FRAME(ee, caller_mb);
    if (mb->fb.access & ACC_STATIC) {
	key = obj_monitor(cbHandle(fieldclass(&mb->fb)));
    } else {
	key = obj_monitor(arg0);
    }
    monitorEnter(key);

    if (mb->args_size == 0) {
	stack_item ret[2];
	optop = (*(stack_item * (*) (stack_item*, ExecEnv*)) mb->code)
	    ((stack_item *)(ret), ee);
    } else {
	optop = (*(stack_item * (*) (stack_item*, ExecEnv*)) mb->code)
	    ((stack_item *)(&arg0), ee);
    }
    monitorExit(key);
    if (exceptionOccurred(ee))
	handle_exception(ee);
    switch(RETURN_TYPE(mb)) {
    case CCF_RETURN_VOID:
	return;
    case CCF_RETURN_INT:
	ASM("mov %0,%%eax" : : "m"(optop[-1].i));
	return;
    case CCF_RETURN_LONG:
	ASM("mov %0,%%eax" : : "m"(optop[-2].i) : "%eax");
	ASM("mov %0,%%edx" : : "m"(optop[-1].i));
	return;
    case CCF_RETURN_FLOAT:
	ASM("flds %0" : : "m"(optop[-1].f));
	return;
    case CCF_RETURN_DOUBLE:
	ASM("fldl %0" : : "m"(optop[-2].f));
	return;
    }
}

#if JDK_VERSION < 12
static int64_t
dispatchJNINativeMethod(struct methodblock *mb, void *arg0, ...)
{
    ExecEnv *ee;
    struct methodblock *caller_mb;
    stack_item *optop;
    struct JNIEnv_ *env;
    int last_base, last_top;
    JNIRefTable *localRefs;
    int argc;
    char *sig;
    jref this_or_clazz;
    int64_t ret;

    GET_CALLER_MB(caller_mb);
    ee = EE();
    SET_COMPILED_FRAME(ee, caller_mb);

    env = &(ee->nativeInterface);
    localRefs = JNIEnvGetLocalRefs(env);
    last_base = localRefs->base;
    last_top = localRefs->top;
    localRefs->base = localRefs->top;

    optop = (stack_item *)(&arg0);        

    if (mb->fb.access & ACC_STATIC) {
        this_or_clazz = MkRefLocal(env, cbHandle(fieldclass(&mb->fb)), JNI_REF_HANDLE_TAG);
    } else {
	this_or_clazz = MkRefLocal(env, arg0, JNI_REF_HANDLE_TAG);
	++optop;
    }
    for (argc = 0, sig = mb->fb.signature; *(++sig) != SIGNATURE_ENDFUNC; argc++) {
        switch (*sig) {
	case SIGNATURE_CLASS:
	    while(*sig != SIGNATURE_ENDCLASS) sig++;
	    optop[argc].p = (void *)MkRefLocal(env, optop[argc].p, JNI_REF_HANDLE_TAG);
	    break;
	case SIGNATURE_ARRAY:
	    while(*sig == SIGNATURE_ARRAY) sig++;
	    if (*sig == SIGNATURE_CLASS) 
	        while(*sig != SIGNATURE_ENDCLASS) sig++;
	    optop[argc].p = (void *)MkRefLocal(env, optop[argc].p, JNI_REF_HANDLE_TAG);
	    break;
	case SIGNATURE_LONG:
	case SIGNATURE_DOUBLE:
	    argc++;
	}
    }
    /* code(env, this_or_clazz, arg, ...) */
    {
	int i;
	for (i = mb->args_size-1; i >= 0; i--)
	    ASM("pushl %0" : : "m"(optop[i].i));
	ASM("pushl %0" : : "m"(this_or_clazz));
	ASM("pushl %0" : : "m"(env));
	ret = (*(int64_t (*) ())mb->code)();
	/* ASM("addl xxx,%sp"); */
    }

    localRefs->base = last_base;
    localRefs->top = last_top;

    if (exceptionOccurred(ee))
	handle_exception(ee);

    sig++;
    switch (*sig) {
	Junion retval;

    case SIGNATURE_BOOLEAN:
	retval.J = ret;
	retval.I.lo = (jboolean)retval.I.lo;
	return retval.J;
    case SIGNATURE_BYTE:
	retval.J = ret;
	retval.I.lo = (jbyte)retval.I.lo;
	return retval.J;
    case SIGNATURE_SHORT:
	retval.J = ret;
	retval.I.lo = (jshort)retval.I.lo;
	return retval.J;
    case SIGNATURE_CHAR:
	retval.J = ret;
	retval.I.lo = (jchar)retval.I.lo;
	return retval.J;
    case SIGNATURE_CLASS:
    case SIGNATURE_ARRAY:
	retval.J = ret;
	retval.I.lo = (unsigned)DeRef(env, retval.I.lo);
	return retval.J;
    case SIGNATURE_FLOAT:
    case SIGNATURE_DOUBLE:
	/* return values are in i387 stack. does nothing */
    case SIGNATURE_LONG:
    default:
	return ret;
    }
}

static int64_t
dispatchJNISynchronizedNativeMethod(struct methodblock *mb, void *arg0, ...)
{
    ExecEnv *ee;
    struct methodblock *caller_mb;
    stack_item *optop;
    struct JNIEnv_ *env;
    int last_base, last_top;
    JNIRefTable *localRefs;
    int argc;
    char *sig;
    jref this_or_clazz;
    int64_t ret;
    JHandle *o;

    GET_CALLER_MB(caller_mb);
    ee = EE();
    SET_COMPILED_FRAME(ee, caller_mb);

    env = &(ee->nativeInterface);
    localRefs = JNIEnvGetLocalRefs(env);
    last_base = localRefs->base;
    last_top = localRefs->top;
    localRefs->base = localRefs->top;

    optop = (stack_item *)(&arg0);        

    if (mb->fb.access & ACC_STATIC) {
        o = (JHandle *)cbHandle(fieldclass(&mb->fb));
    } else {
	o = arg0;
	++optop;
    }
    this_or_clazz = MkRefLocal(env, o, JNI_REF_HANDLE_TAG);
    for (argc = 0, sig = mb->fb.signature; *(++sig) != SIGNATURE_ENDFUNC; argc++) {
        switch (*sig) {
	case SIGNATURE_CLASS:
	    while(*sig != SIGNATURE_ENDCLASS) sig++;
	    optop[argc].p = (void *)MkRefLocal(env, optop[argc].p, JNI_REF_HANDLE_TAG);
	    break;
	case SIGNATURE_ARRAY:
	    while(*sig == SIGNATURE_ARRAY) sig++;
	    if (*sig == SIGNATURE_CLASS) 
	        while(*sig != SIGNATURE_ENDCLASS) sig++;
	    optop[argc].p = (void *)MkRefLocal(env, optop[argc].p, JNI_REF_HANDLE_TAG);
	    break;
	case SIGNATURE_LONG:
	case SIGNATURE_DOUBLE:
	    argc++;
	}
    }
    monitorEnter(obj_monitor(o));
    /* code(env, this_or_clazz, arg, ...) */
    {
	int i;
	for (i = mb->args_size-1; i >= 0; i--)
	    ASM("pushl %0" : : "m"(optop[i].i));
	ASM("pushl %0" : : "m"(this_or_clazz));
	ASM("pushl %0" : : "m"(env));
	ret = (*(int64_t (*) ())mb->code)();
	/* ASM("addl xxx,%sp"); */
    }
    monitorExit(obj_monitor(o));

    localRefs->base = last_base;
    localRefs->top = last_top;

    if (exceptionOccurred(ee))
	handle_exception(ee);

    sig++;
    switch (*sig) {
	Junion retval;

    case SIGNATURE_BOOLEAN:
	retval.J = ret;
	retval.I.lo = (jboolean)retval.I.lo;
	return retval.J;
    case SIGNATURE_BYTE:
	retval.J = ret;
	retval.I.lo = (jbyte)retval.I.lo;
	return retval.J;
    case SIGNATURE_SHORT:
	retval.J = ret;
	retval.I.lo = (jshort)retval.I.lo;
	return retval.J;
    case SIGNATURE_CHAR:
	retval.J = ret;
	retval.I.lo = (jchar)retval.I.lo;
	return retval.J;
    case SIGNATURE_CLASS:
    case SIGNATURE_ARRAY:
	retval.J = ret;
	retval.I.lo = (unsigned)DeRef(env, retval.I.lo);
	return retval.J;
    case SIGNATURE_FLOAT:
    case SIGNATURE_DOUBLE:
	/* %f0 & %f1 must be preserved here */
    case SIGNATURE_LONG:
    default:
	return ret;
    }
}
#else /* JDK1.2 */
static int
dispatchJNINativeMethod(struct methodblock *mb, void *arg0, ...)
{
    ExecEnv *ee;
    char *sig;
    struct methodblock *caller_mb;
    JavaFrame  *old_frame;	/* previous frame */
    stack_item *optop;		/* Points to just before args */
    JavaStack  *stack;
    JavaFrame  *frame;
    JNIEnv *env;
    JHandle **this_or_clazz;
    int argc;
    int32_t eax, edx;

    GET_CALLER_MB(caller_mb);
    ee = EE();
    old_frame = ee->current_frame;
    SET_COMPILED_FRAME(ee, caller_mb);
    stack = old_frame->javastack; 
    frame = (JavaFrame *)(old_frame->optop + mb->args_size);

    if (frame->ostack + JNI_REF_INFO_SIZE + JNI_DEFAULT_LOCAL_CAPACITY >= 
	stack->end_data) {
        JavaStack *tmp_stack = stack;
	JavaFrame *tmp_frame = frame;
	if (!ExpandJavaStackForJNI(ee, &tmp_stack, &tmp_frame,
		JNI_REF_INFO_SIZE + JNI_DEFAULT_LOCAL_CAPACITY))
	    handle_exception(ee);
	stack = tmp_stack;
	frame = tmp_frame;
    }

    frame->javastack = stack;
    frame->prev = old_frame;
    frame->optop = frame->ostack + JNI_REF_INFO_SIZE;
    frame->current_method = mb;

    JNI_REFS_FREELIST(frame) = NULL;
    JNI_N_REFS_IN_USE(frame) = 0;
    JNI_REFS_CAPACITY(frame) = JNI_DEFAULT_LOCAL_CAPACITY;

    ee->current_frame = frame;

    optop = (stack_item *)&arg0;

    if (mb->fb.access & ACC_STATIC) {
	this_or_clazz = (JHandle **)(&cbHandle(fieldclass(&mb->fb)));
    } else {
	this_or_clazz = (JHandle **)&arg0;
	++optop;
    }

    env = EE2JNIEnv(ee);
    /* copy args */
    ASM("subl %0,%%esp" : : "r" (mb->args_size * sizeof(int32_t)));
    sig = mb->terse_signature;
    for (argc = 0; *sig != TERSE_SIG_ENDFUNC; argc++, sig++) {
	if (*sig == TERSE_SIG_OBJECT) {
	    ASM("movl %1,0x0(%%esp,%0,4)" : : "r"(argc),
		"r"((optop[argc].p == NULL) ? 0 : (uint32_t)(&optop[argc].i)));
	} else {
	    ASM("movl %1,0x0(%%esp,%0,4)" : : "r"(argc), "r"(optop[argc].i));
	    if (*sig == TERSE_SIG_LONG || *sig == TERSE_SIG_DOUBLE) {
		++argc;
		ASM("movl %1,0x0(%%esp,%0,4)" : : "r"(argc), "r"(optop[argc].i));
	    }
	}
    }
    sig++;
    ASM("pushl %0" : : "m"(this_or_clazz));
    ASM("pushl %0" : : "m"(env));
    eax = (*(int (*) ())mb->code)();
    ASM("movl %%edx,%0" : "=r"(edx) :);
    /* floating register must be preserved from here until return */
    ee->current_frame = old_frame;
    if (exceptionOccurred(ee))
	handle_exception(ee);
    if (*sig == TERSE_SIG_OBJECT) {
	eax = (unsigned)DeRef(env, eax);
	return eax;
    }
    ASM("movl %0,%%edx" : : "r"(edx));
    return eax;
}

static int32_t
dispatchJNISynchronizedNativeMethod(struct methodblock *mb, void *arg0, ...)
{
    ExecEnv *ee;
    char *sig;
    struct methodblock *caller_mb;
    JavaFrame  *old_frame;	/* previous frame */
    stack_item *optop;		/* Points to just before args */
    JavaStack  *stack;
    JavaFrame  *frame;
    JNIEnv *env;
    JHandle **this_or_clazz;
    int argc;
    int32_t eax, edx;

    GET_CALLER_MB(caller_mb);
    ee = EE();
    old_frame = ee->current_frame;
    SET_COMPILED_FRAME(ee, caller_mb);
    stack = old_frame->javastack; 
    frame = (JavaFrame *)(old_frame->optop + mb->args_size);

    if (frame->ostack + JNI_REF_INFO_SIZE + JNI_DEFAULT_LOCAL_CAPACITY >= 
	stack->end_data) {
        JavaStack *tmp_stack = stack;
	JavaFrame *tmp_frame = frame;
	if (!ExpandJavaStackForJNI(ee, &tmp_stack, &tmp_frame,
		JNI_REF_INFO_SIZE + JNI_DEFAULT_LOCAL_CAPACITY))
	    handle_exception(ee);
	stack = tmp_stack;
	frame = tmp_frame;
    }

    frame->javastack = stack;
    frame->prev = old_frame;
    frame->optop = frame->ostack + JNI_REF_INFO_SIZE;
    frame->current_method = mb;

    JNI_REFS_FREELIST(frame) = NULL;
    JNI_N_REFS_IN_USE(frame) = 0;
    JNI_REFS_CAPACITY(frame) = JNI_DEFAULT_LOCAL_CAPACITY;

    ee->current_frame = frame;

    optop = (stack_item *)&arg0;

    if (mb->fb.access & ACC_STATIC) {
	this_or_clazz = (JHandle **)(&cbHandle(fieldclass(&mb->fb)));
    } else {
	this_or_clazz = (JHandle **)&arg0;
	++optop;
    }

    env = EE2JNIEnv(ee);
    monitorEnter2(ee, obj_monitor(*this_or_clazz));
    /* copy args */
    ASM("subl %0,%%esp" : : "r" (mb->args_size * sizeof(int32_t)));
    sig = mb->terse_signature;
    for (argc = 0; *sig != TERSE_SIG_ENDFUNC; argc++, sig++) {
	if (*sig == TERSE_SIG_OBJECT) {
	    ASM("movl %1,0x0(%%esp,%0,4)" : : "r"(argc),
		"r"((optop[argc].p == NULL) ? 0 : (uint32_t)(&optop[argc].i)));
	} else {
	    ASM("movl %1,0x0(%%esp,%0,4)" : : "r"(argc), "r"(optop[argc].i));
	    if (*sig == TERSE_SIG_LONG || *sig == TERSE_SIG_DOUBLE) {
		++argc;
		ASM("movl %1,0x0(%%esp,%0,4)" : : "r"(argc), "r"(optop[argc].i));
	    }
	}
    }
    sig++;
    ASM("pushl %0" : : "m"(this_or_clazz));
    ASM("pushl %0" : : "m"(env));
    eax = (*(int (*) ())mb->code)();
    ASM("movl %%edx,%0" : "=r"(edx) :);
    monitorExit2(ee, obj_monitor(*this_or_clazz));
    /* floating register must be preserved from here until return */
    ee->current_frame = old_frame;
    if (exceptionOccurred(ee))
	handle_exception(ee);
    if (*sig == TERSE_SIG_OBJECT) {
	eax = (unsigned)DeRef(env, eax);
	return eax;
    }
    ASM("movl %0,%%edx" : : "r"(edx));
    return eax;
}
#endif

long
dispatchJVM(struct methodblock *mb, void *arg0, ...)
{
    ExecEnv *ee;
    struct methodblock *caller_mb;
    stack_item *optop;
    void *obj = NULL;
    long result;
    bool_t isStaticCall;
    long hi_word;

    GET_CALLER_MB(caller_mb);
    ee = EE();
    SET_COMPILED_FRAME(ee, caller_mb);

    optop = (stack_item *)(&arg0);
    if (mb->fb.access & ACC_STATIC) {
	isStaticCall = TRUE;
    } else {
	isStaticCall = FALSE;
	obj = optop[0].h;
	++optop;
    }
    
    result = do_execute_java_method_vararg(ee, obj, 0, 0, mb, isStaticCall,
					   optop, &hi_word, TRUE);
    /* register %f0 & %f1 must be preserved from here until return */;
    if (exceptionOccurred(ee))
	handle_exception(ee);
    switch(RETURN_TYPE(mb)) {
    case CCF_RETURN_DOUBLE:
	{
	    double d;

	    ((int *)&d)[0] = hi_word;
	    ((int *)&d)[1] = result;
	    ASM("fldl %0" : : "m"(d));
	    ASM("leave; ret");
	}
    case CCF_RETURN_FLOAT:
	ASM("flds %0" : : "m"(result));
	ASM("leave; ret");
    case CCF_RETURN_LONG:
	/* return 64bit value */
	ASM("mov %0,%%edx" : : "m"(result) : "%edx");
	ASM("mov %0,%%eax" : : "m"(hi_word) : "%eax");
	ASM("leave; ret");
 /* case CCF_RETURN_INT: */
    default:
	return result;
    }
}

void dispatchJITCompiler(struct methodblock *mb, ...)
{
    ExecEnv *ee;

    ee = EE();
    if (OpenJIT_compile(ee, mb)) {
	SET_COMPILED_FRAME(ee, mb);
	JUMP_IN_COMPILED_CODE(mb);
    } else {
	ASM("leave;jmp dispatchJVM");
    }
}

void
OpenJIT_resolveNativeMethod(struct methodblock *mb)
{
#if JDK_VERSION < 12
    if (mb->code == 0) {
	monitorEnter(obj_monitor(cbHandle(mb->fb.clazz)));
	if (dynoLink(mb)) {
	    mb->invoker = (mb->fb.access & ACC_SYNCHRONIZED)
		? invokeSynchronizedNativeMethod : invokeNativeMethod;
	} else if (dynoLinkJNI(mb)) {
	    mb->invoker = (mb->fb.access & ACC_SYNCHRONIZED)
		? invokeJNISynchronizedNativeMethod : invokeJNINativeMethod;
	}
	monitorExit(obj_monitor(cbHandle(mb->fb.clazz)));
	if (mb->code == 0)
	    OpenJIT_Error(JAVAPKG "UnsatisfiedLinkError", fieldname(&mb->fb));
    }
#else  /* JDK1.2 */
    void *code;
    ExecEnv *ee = EE();
    sys_thread_t *self = EE2SysThread(ee);

    LINKCLASS_LOCK(self);
    code = mb->code;
    LINKCLASS_UNLOCK(self);

    if (code == NULL) {
        uint32_t lib_info;
    	code = dynoLink(mb, &lib_info);
	if (code == NULL) {
	    OpenJIT_Error(JAVAPKG "UnsatisfiedLinkError", fieldname(&mb->fb));
	} else {
	    LINKCLASS_LOCK(self);
	    if (mb->code == NULL) {
	        mb->code = code;
	        if (lib_info != 0) {
		    if (mb->fb.access & ACC_SYNCHRONIZED) {
			mb->invoker = invokeJNISynchronizedNativeMethod;
		    } else {
		        Invoker inv = getCustomInvoker(methodTerseSig(mb));
			if (inv)
			    mb->invoker = inv;
			else
			    mb->invoker = invokeJNINativeMethod;
		    }
		} else {
		    mb->invoker = (mb->fb.access & ACC_SYNCHRONIZED) 
		        ? invokeSynchronizedNativeMethod : invokeNativeMethod;
		}
	    }
	    LINKCLASS_UNLOCK(self);
	}
    }
#endif
    if (mb->fb.access & ACC_SYNCHRONIZED) {
	mb->CompiledCode = (mb->invoker == invokeSynchronizedNativeMethod)
	    ? dispatchSyncNative : (void *)dispatchJNISynchronizedNativeMethod;
    } else {
	if (mb->invoker == invokeNativeMethod) {
	    if (mb->args_size == 0) {
		switch(RETURN_TYPE(mb)) {
		case CCF_RETURN_LONG:
		    mb->CompiledCode = dispatchNormNativeJ_noarg;
		    break;
		case CCF_RETURN_DOUBLE:
		    mb->CompiledCode = dispatchNormNativeD_noarg;
		    break;
		default:
		    mb->CompiledCode = dispatchNormNativeTable[RETURN_TYPE(mb)];
		}
	    } else {
		mb->CompiledCode = dispatchNormNativeTable[RETURN_TYPE(mb)];
	    }
	} else
	    mb->CompiledCode = dispatchJNINativeMethod;
    }
    FIX_ENTRY(mb);
}

void
dispatchLazyNativeMethod(struct methodblock *mb, ...)
{
    OpenJIT_resolveNativeMethod(mb);
    JUMP_IN_COMPILED_CODE(mb);
}


static void ATTRIBUTE((stdcall, regparm(1)))
OpenJIT_getstatic(int index)
{
    struct methodblock	*caller_mb;
    cp_item_type	*constant_pool;
    struct fieldblock	*fb;
    long *addr;

    CHECK_PATCHING_CODE();
    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    RESOLVE_CLASS_CONST(constant_pool, index, CONSTANT_Fieldref, caller_mb);
    fb = constant_pool[index].p;

    if ((fb->access & ACC_STATIC) == 0) {
	char buf[256];
	int len;
	slash2dot(cbName(fieldclass(fb)), buf, sizeof(buf));
	len = strlen(buf);
	/* If buffer overflow, quietly truncate */
	(void) jio_snprintf(buf + len, sizeof(buf)-len,
			    ": field %s used to be static",
			    fb->name);
	OpenJIT_Error(JAVAPKG "IncompatibleClassChangeError", buf);
    }

#ifdef CODE_PATCH
    {
	volatile unsigned char *caller_pc;

	ASM("movl 0x4(%%ebp),%0" : "=r"(caller_pc));
	/* back to call inst */
	caller_pc -= 5;
	/* At first, change "call" to "int $0x3" */
	caller_pc[0] = 0xcc;

	switch(fb->signature[0]) {
	case SIGNATURE_DOUBLE:
	    addr = (long *)(fb->u.static_address);
	    /* fldl addr */
	    *(uint32_t *)(caller_pc+2) = (uint32_t)(addr);
	    caller_pc[1] = 0x05;
	    caller_pc[0] = 0xdd;
	    break;
	case SIGNATURE_FLOAT:
	    addr = (long *)&(fb->u.static_value);
	    /* flds addr */
	    *(uint32_t *)(caller_pc+2) = (uint32_t)(addr);
	    caller_pc[1] = 0x05;
	    caller_pc[0] = 0xd9;
	    break;
	case SIGNATURE_LONG:
	    addr = (long *)(fb->u.static_address);
	    /* movl addr+4, %eax */
	    /* movl addr, %edx */
	    *(uint32_t *)(caller_pc+1) = (uint32_t)(addr);
	    *(uint32_t *)(caller_pc+7) = (uint32_t)(addr+1);
	    caller_pc[5] = 0x8b;
	    caller_pc[6] = 0x15;
	    caller_pc[0] = 0xa1;
	    break;
	default:
	    addr = (long *)&(fb->u.static_value);
	    /* movl addr, %eax */
	    *(uint32_t *)(caller_pc+1) = (uint32_t)(addr);
	    caller_pc[0] = 0xa1;
	}
	/* fix return address */
	ASM("subl $0x5,0x4(%ebp)");
	return;
    }
#else
    switch(fb->signature[0]) {
    case SIGNATURE_DOUBLE:
	addr = (long *)(fb->u.static_address);
	ASM("fldl %0" : : "m"(addr[0]));
	return;
    case SIGNATURE_FLOAT:
	ASM("flds %0" : : "m"(fb->u.static_value));
	return;
    case SIGNATURE_LONG:
	addr = (long *)(fb->u.static_address);
	ASM("mov %0,%%edx" : : "m"(addr[1]) : "%edx");
	ASM("mov %0,%%eax" : : "m"(addr[0]));
	return;
    default:
	ASM("mov %0,%%eax" : : "m"(fb->u.static_value));
	return;
    }
#endif
}

static int ATTRIBUTE((stdcall, regparm(1)))
OpenJIT_resolveStaticField(int index)
{
    struct methodblock	*caller_mb;
    cp_item_type	*constant_pool;
    struct fieldblock	*fb;

    CHECK_PATCHING_CODE();
    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    RESOLVE_CLASS_CONST(constant_pool, index, CONSTANT_Fieldref, caller_mb);
    fb = constant_pool[index].p;

    if ((fb->access & ACC_STATIC) == 0) {
	char buf[256];
	int len;
	slash2dot(cbName(fieldclass(fb)), buf, sizeof(buf));
	len = strlen(buf);
	/* If buffer overflow, quietly truncate */
	(void) jio_snprintf(buf + len, sizeof(buf)-len,
			    ": field %s used to be static",
			    fb->name);
	OpenJIT_Error(JAVAPKG "IncompatibleClassChangeError", buf);
    }

    if (fb->signature[0] == SIGNATURE_LONG || fb->signature[0] == SIGNATURE_DOUBLE) {
	PATCH_CODE_FIELD_ACCESS(fb->u.static_address);
	return (int)(fb->u.static_address);
    } else {
	PATCH_CODE_FIELD_ACCESS(&fb->u.static_address);
	return (int)(&fb->u.static_value);
    }
}

static int ATTRIBUTE((stdcall, regparm(1)))
OpenJIT_resolveField(int index)
{
    struct methodblock	*caller_mb;
    cp_item_type	*constant_pool;
    struct fieldblock	*fb;

    CHECK_PATCHING_CODE();
    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    RESOLVE_CLASS_CONST(constant_pool, index, CONSTANT_Fieldref, caller_mb);
    fb = constant_pool[index].p;

    if (fb->access & ACC_STATIC) {
	char buf[256];
	int len;
	slash2dot(cbName(fieldclass(fb)), buf, sizeof(buf));
	len = strlen(buf);
	/* If buffer overflow, quietly truncate */
	(void) jio_snprintf(buf + len, sizeof(buf)-len,
			    ": field %s did not used to be static",
			    fb->name);
	OpenJIT_Error(JAVAPKG "IncompatibleClassChangeError", buf);
    }
    PATCH_CODE_FIELD_ACCESS(fb->u.offset);
    return fb->u.offset;
}

static int ATTRIBUTE((stdcall, regparm(1)))
OpenJIT_resolveString(int index)
{
    struct methodblock	*caller_mb;
    cp_item_type	*constant_pool;

    CHECK_PATCHING_CODE();
    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    RESOLVE_CLASS_CONST(constant_pool, index, CONSTANT_String, caller_mb);
    PATCH_CODE_FIELD_ACCESS(constant_pool[index].i);
    return constant_pool[index].i;
}

static HObject * ATTRIBUTE((stdcall))
OpenJIT_new_quick(ClassClass *cb)
{
    HObject *ret;
    if ((ret = ObjAlloc(cb, 0)) == 0) {
	char buf[256];
	OpenJIT_Error(JAVAPKG "OutOfMemoryError", slash2dot(cbName(cb), buf, sizeof(buf)));
    }
    return ret;
}

static HObject * ATTRIBUTE((stdcall))
OpenJIT_new(int index)
{
    struct methodblock	*caller_mb;
    cp_item_type	*constant_pool;
    ClassClass		*cb, *current_class;
    HObject 		*ret;

    CHECK_PATCHING_CODE();
    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    RESOLVE_CLASS_CONST(constant_pool, index, CONSTANT_Class, caller_mb);
    cb = constant_pool[index].p;
#if JDK_VERSION >=12
    InitClass(cb);
#endif
    if (cbAccess(cb) & (ACC_INTERFACE | ACC_ABSTRACT)) {
	OpenJIT_Error(JAVAPKG "InstantiationError", cbName(cb));
    }
    current_class = caller_mb ? fieldclass(&caller_mb->fb) : NULL;
    if (!VerifyClassAccess(current_class, cb, FALSE)) {
	OpenJIT_Error(JAVAPKG "IllegalAccessError", cbName(cb));
    }
    PATCH_CODE_PUSH_AND_CALL(OpenJIT_new_quick, cb);
    if ((ret = ObjAlloc(cb, 0)) == 0) {
	char buf[256];
	OpenJIT_Error(JAVAPKG "OutOfMemoryError", slash2dot(cbName(cb), buf, sizeof(buf)));
    }
    return ret;
}

static HArrayOfObject * ATTRIBUTE((stdcall))
OpenJIT_anewarray_quick(ClassClass *array_cb, int size)
{
    HArrayOfObject *ret;

    if (size < 0) {
	OpenJIT_Error(JAVAPKG "NegativeArraySizeException", 0);
    }
    ret = (HArrayOfObject *)ArrayAlloc(T_CLASS, size);
    if (ret == 0) {
	OpenJIT_Error(JAVAPKG "OutOfMemoryError", 0);
    } else {
	unhand(ret)->body[size] = (HObject*)array_cb;
    }
    return ret;
}

static HArrayOfObject * ATTRIBUTE((stdcall))
OpenJIT_anewarray(int index, int size)
{
    ClassClass *array_cb;

    CHECK_PATCHING_CODE();
    RESOLVE_CLASS_CLASS(index, OpenJIT_anewarray_quick, array_cb);
    return OpenJIT_anewarray_quick(array_cb, size);
}

static HObject *
OpenJIT_multianewarray_quick(ClassClass *array_cb, int dimensions, ...)
{
    va_list ap;
    HObject *ret;
    int i;

    va_start(ap, dimensions);
    for (i = 0; i < dimensions; i++) {
	int dim = va_arg(ap, int);
	if (dim < 0)
	    OpenJIT_Error(JAVAPKG "NegativeArraySizeException", 0);
    }
    va_end(ap);
    ret = MultiArrayAlloc(dimensions, array_cb, (stack_item *)((int)(&dimensions)+4));
    if (ret == 0)
	OpenJIT_Error(JAVAPKG "OutOfMemoryError", 0);
    return ret;
}

static HObject *
OpenJIT_multianewarray(int index, int dimensions, ...)
{
    va_list ap;
    ClassClass *array_cb;
    HObject *ret;
    int i;

    CHECK_PATCHING_CODE();
    RESOLVE_CLASS_CLASS(index, OpenJIT_multianewarray_quick, array_cb);

    va_start(ap, dimensions);
    for (i = 0; i < dimensions; i++) {
	int dim = va_arg(ap, int);
	if (dim < 0)
	    OpenJIT_Error(JAVAPKG "NegativeArraySizeException", 0);
    }
    va_end(ap);
    ret = MultiArrayAlloc(dimensions, array_cb, (stack_item *)((int)(&dimensions)+4));
    if (ret == 0)
	OpenJIT_Error(JAVAPKG "OutOfMemoryError", 0);
    return ret;
}

static void ATTRIBUTE((stdcall))
OpenJIT_check_aastore(HObject *obj, HArrayOfObject *arr)
{
    ClassClass *cb;
    cb = (ClassClass *)(unhand(arr)->body[obj_length(arr)]);
    if (!IS_INSTANCE_OF(obj, cb, EE())) {
	OpenJIT_Error(JAVAPKG "ArrayStoreException", 0);
    }
}

static void ATTRIBUTE((stdcall))
OpenJIT_checkcast_quick(ClassClass *cb, JHandle *h)
{
    if (!IS_INSTANCE_OF(h, cb, EE())) {
	/* Checkcast fails  */
	char buf[256];
	OpenJIT_Error(JAVAPKG "ClassCastException", 
	      slash2dot(cbName(obj_array_classblock(h)), 
			       buf, sizeof(buf)));
    }
}

static void ATTRIBUTE((stdcall))
OpenJIT_checkcast(int index, JHandle *h)
{
    ClassClass *cb;

    CHECK_PATCHING_CODE();
    RESOLVE_CLASS_CLASS(index, OpenJIT_checkcast_quick, cb);
    OpenJIT_checkcast_quick(cb, h);
}

static bool_t ATTRIBUTE((stdcall))
OpenJIT_instanceof_quick(ClassClass *cb, JHandle *h)
{
    return ((h != NULL) && IS_INSTANCE_OF(h, cb, EE()));
}

static bool_t ATTRIBUTE((stdcall))
OpenJIT_instanceof(int index, JHandle *h)
{
    ClassClass *cb;

    CHECK_PATCHING_CODE();
    RESOLVE_CLASS_CLASS(index, OpenJIT_instanceof_quick, cb);
    return ((h != NULL) && IS_INSTANCE_OF(h, cb, EE()));
}

static int
OpenJIT_lcmp(int64_t x, int64_t y)
{
    if (x > y)
	return 1;
    if (x == y)
	return 0;
    else
	return -1;
}

static int
OpenJIT_fcmpl(float l, float r)
{
    return (l < r) ? -1 : (l > r) ? 1 : (l == r ) ?  0 
	/* Beyond here, must be NaN. */
	: -1;
}

static int
OpenJIT_fcmpg(float l, float r)
{
    return (l < r) ? -1 : (l > r) ? 1 : (l == r ) ?  0 
	/* Beyond here, must be NaN. */
	: 1;
}

static int
OpenJIT_dcmpl(double l, double r)
{
    return (l < r) ? -1 : (l > r) ? 1 : (l == r ) ?  0 
	/* Beyond here, must be NaN. */
	: -1;
}

static int
OpenJIT_dcmpg(double l, double r)
{
    return (l < r) ? -1 : (l > r) ? 1 : (l == r ) ?  0 
	/* Beyond here, must be NaN. */
	: 1;
}

static float
OpenJIT_frem(float x, float y)
{
    return (float)fmod((double)x, (double)y);
}

static void
OpenJIT_invokesuper_quick(struct methodblock *mb, ...)
{
    struct methodblock *caller_mb;
    ClassClass *super_class;

    GET_CALLER_MB(caller_mb);
    super_class = cbSuperclass(fieldclass(&caller_mb->fb));

    mb = cbMethodTable(super_class)->methods[mb->fb.u.offset];
    JUMP_IN_COMPILED_CODE(mb);
}

#if JDK_VERSION >= 12
#define isSpecialSuperCall(X, Y) _isSpecialSuperCall(X,Y)
static bool_t
_isSpecialSuperCall(ClassClass *current_class, struct methodblock *mb) {
    if (((cbAccess(current_class) & ACC_SUPER) != 0)
	  && (!IsPrivate(mb->fb.access))
	  && (strcmp(mb->fb.name, "<init>") != 0)
	  && (current_class != classJavaLangObject)) {
	ClassClass *methodClass = fieldclass(&mb->fb);
	ClassClass *cb = cbSuperclass(current_class);
	for (; ; cb = cbSuperclass(cb)) {
	    if (cb == methodClass) 
		return TRUE;
	    if (cbSuperclass(cb) == 0)
		return FALSE;
	}
    }
    return FALSE;
}
#endif

static void
OpenJIT_invokespecial(struct methodblock *mb, ...)
{
    struct methodblock *caller_mb;
    ClassClass *current_class;

    GET_CALLER_MB(caller_mb);
    current_class = caller_mb ? 
	fieldclass(&caller_mb->fb) : NULL;

    if ((mb->fb.access & ACC_STATIC) != 0) {
	char buf[256];
	int len;
	slash2dot(cbName(fieldclass(&mb->fb)), buf, sizeof(buf));
	len = strlen(buf);
	(void )jio_snprintf(buf + len, sizeof(buf)-len, 
			    ": method %s%s did not used to be static",
			    mb->fb.name, mb->fb.signature);
	OpenJIT_Error(JAVAPKG "IncompatibleClassChangeError", buf);
    }
    if ((mb->fb.access & ACC_ABSTRACT) != 0) {
	OpenJIT_Error(JAVAPKG "AbstractMethodError", fieldname(&mb->fb));
    }

    if ((current_class != NULL) && isSpecialSuperCall(current_class, mb)) {
	ClassClass *super_class = cbSuperclass(current_class);
	struct methodblock *new_mb = cbMethodTable(super_class)->methods[mb->fb.u.offset];
	if (mb != new_mb) {
	    PATCH_CODE_CALL(OpenJIT_invokesuper_quick);
	    *((struct methodblock * volatile *)&mb) = new_mb;
	    JUMP_IN_COMPILED_CODE(new_mb);
	}
    }
    RESOLVE_NATIVE_OR_COMPILE(mb);
#ifdef CODE_PATCH
    if (ENTRY_IS_FIXED(mb)) {
	PATCH_CODE_CALL(mb->CompiledCode);
    }
#endif
    JUMP_IN_COMPILED_CODE(mb);
}

static void
OpenJIT_invokespecial_resolve(unsigned int index, ...)
{
    struct methodblock *caller_mb;
    ClassClass *current_class;
    struct methodblock *mb;
    cp_item_type *constant_pool;

    CHECK_PATCHING_CODE();
    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    RESOLVE_CLASS_CONST(constant_pool, index, CONSTANT_Methodref, caller_mb);
    mb = constant_pool[index].mb;

    current_class = caller_mb ? 
	fieldclass(&caller_mb->fb) : NULL;

    if ((mb->fb.access & ACC_STATIC) != 0) {
	char buf[256];
	int len;
	slash2dot(cbName(fieldclass(&mb->fb)), buf, sizeof(buf));
	len = strlen(buf);
	(void )jio_snprintf(buf + len, sizeof(buf)-len, 
			    ": method %s%s did not used to be static",
			    mb->fb.name, mb->fb.signature);
	OpenJIT_Error(JAVAPKG "IncompatibleClassChangeError", buf);
    }
    if ((mb->fb.access & ACC_ABSTRACT) != 0) {
	OpenJIT_Error(JAVAPKG "AbstractMethodError", fieldname(&mb->fb));
    }

    if ((current_class != NULL) && isSpecialSuperCall(current_class, mb)) {
	ClassClass *super_class = cbSuperclass(current_class);
	struct methodblock *new_mb = cbMethodTable(super_class)->methods[mb->fb.u.offset];
	if (mb != new_mb) {
	    PATCH_CODE_PUSH_AND_CALL(mb->CompiledCode, mb);
	    *((struct methodblock * volatile *)&index) = new_mb;
	    JUMP_IN_COMPILED_CODE(new_mb);
	}
    }
    RESOLVE_NATIVE_OR_COMPILE(mb);
#ifdef CODE_PATCH
    if (ENTRY_IS_FIXED(mb)) {
	PATCH_CODE_PUSH_AND_CALL(mb->CompiledCode, mb);
    }
#endif
    *((struct methodblock * volatile *)&index) = mb;
    JUMP_IN_COMPILED_CODE(mb);
}

#ifndef INLINE_INVOKE_VIRTUAL
static void
OpenJIT_invokevirtual_quick(int offset, void *arg0, ...)
{
    struct methodblock *mb;

    mb = mt_slot(obj_methodtable((JHandle *)arg0), offset);
    *((struct methodblock * volatile*)&offset) = mb;
    JUMP_IN_COMPILED_CODE(mb);
}
#endif

static void
OpenJIT_invokevirtualobject_quick(int offset, void *arg0, ...)
{
    struct methodblock *mb;

    mb = mt_slot(obj_array_methodtable((JHandle *)arg0), offset);
    *((struct methodblock * volatile *)&offset) = mb;
    JUMP_IN_COMPILED_CODE(mb);
}

static void
OpenJIT_invokevirtual_resolve(int index, void *arg0, ...)
{
    struct methodblock *mb;
    struct methodblock *caller_mb;
    cp_item_type *constant_pool;

    CHECK_PATCHING_CODE();
    
    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    RESOLVE_CLASS_CONST(constant_pool, index, CONSTANT_Methodref, caller_mb);
    mb = constant_pool[index].mb;
    if ((mb->fb.access & ACC_STATIC) != 0) {
	char buf[256];
	int len;
	slash2dot(cbName(fieldclass(&mb->fb)), buf, sizeof(buf));
	len = strlen(buf);
	(void )jio_snprintf(buf + len, sizeof(buf)-len, 
			    ": method %s%s did not used to be static",
			    mb->fb.name, mb->fb.signature);
	OpenJIT_Error(JAVAPKG "IncompatibleClassChangeError", buf);
    }
    if ((mb->fb.access & (ACC_PRIVATE | ACC_FINAL)) != 0) {
	RESOLVE_NATIVE_OR_COMPILE(mb);
	if (ENTRY_IS_FIXED(mb)) {
	    PATCH_CODE_PUSH_AND_CALL(mb->CompiledCode, mb);
	}
    } else {
	/* int offset = (mb->fb.u.offset << 2) + offsetof(JHandle, methods); */
	if (fieldclass(&mb->fb) == classJavaLangObject) {
	    PATCH_CODE_PUSH_AND_CALL(OpenJIT_invokevirtualobject_quick, mb->fb.u.offset);
	    mb = mt_slot(obj_array_methodtable((JHandle *)arg0), mb->fb.u.offset);
	} else {
#ifdef INLINE_INVOKE_VIRTUAL
	    volatile unsigned char *caller_pc;
	    ASM("movl 0x4(%%ebp),%0" : "=r"(caller_pc));
	    /* back to call inst */
	    caller_pc -= 10;
	    /* At first, change "push" to "int $0x3" */
	    caller_pc[0] = 0xcc; /* 0x8b */
	    caller_pc[1] = 0x80; /* movl offset(%eax),%eax */
	    *(volatile int *)(caller_pc+2) = mb->fb.u.offset * 4 + 4;
	    
	    caller_pc[6] = 0x50; /* pushl %eax */
	    caller_pc[7] = 0xff; /* call *0x44(%eax) */
	    caller_pc[8] = 0x50;
	    caller_pc[9] = offsetof(struct methodblock, CompiledCode);
	    caller_pc[0] = 0x8b;
#else
	    PATCH_CODE_PUSH_AND_CALL(OpenJIT_invokevirtual_quick, mb->fb.u.offset);
#endif
	    mb = mt_slot(obj_methodtable((JHandle *)arg0), mb->fb.u.offset);
	}
	RESOLVE_NATIVE_OR_COMPILE(mb);
    }
    *((struct methodblock * volatile *)&index) = mb;

    JUMP_IN_COMPILED_CODE(mb);
}

static void
OpenJIT_invokestatic_resolve(unsigned int index, ...)
{
    struct methodblock *mb;
    struct methodblock *caller_mb;
    cp_item_type *constant_pool;

    CHECK_PATCHING_CODE();
    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    RESOLVE_CLASS_CONST(constant_pool, index, CONSTANT_Methodref, caller_mb);
    mb = constant_pool[index].mb;

    if ((mb->fb.access & ACC_STATIC) == 0) {
	char buf[256];
	int len;
	slash2dot(cbName(fieldclass(&mb->fb)), buf, sizeof(buf));
	len = strlen(buf);
	(void )jio_snprintf(buf + len, sizeof(buf)-len, 
			    ": method %s%s used to be static",
			    mb->fb.name, mb->fb.signature);
	OpenJIT_Error(JAVAPKG "IncompatibleClassChangeError", buf);
    }

    RESOLVE_NATIVE_OR_COMPILE(mb);
#ifdef CODE_PATCH
    if (ENTRY_IS_FIXED(mb)) {
	PATCH_CODE_PUSH_AND_CALL(mb->CompiledCode, mb);
    }
#endif
    *((struct methodblock * volatile *)&index) = mb;
    JUMP_IN_COMPILED_CODE(mb);
}

static void
OpenJIT_invokestatic(struct methodblock *mb, ...)
{
    if ((mb->fb.access & ACC_STATIC) == 0) {
	char buf[256];
	int len;
	slash2dot(cbName(fieldclass(&mb->fb)), buf, sizeof(buf));
	len = strlen(buf);
	(void )jio_snprintf(buf + len, sizeof(buf)-len, 
			    ": method %s%s used to be static",
			    mb->fb.name, mb->fb.signature);
	OpenJIT_Error(JAVAPKG "IncompatibleClassChangeError", buf);
    }

    RESOLVE_NATIVE_OR_COMPILE(mb);
#ifdef CODE_PATCH
    if (ENTRY_IS_FIXED(mb)) {
	PATCH_CODE_CALL(mb->CompiledCode);
    }
#endif
    JUMP_IN_COMPILED_CODE(mb);
}

static void
OpenJIT_invokeinterface_quick(int index, void *arg0, ...)
{
    JHandle *o = arg0;
    struct methodblock *mb;
    struct methodblock *caller_mb;
    cp_item_type *constant_pool;

    struct methodblock *imb;
    ClassClass *intf;
    unsigned offset;
    int guess;
    ClassClass *cb;
    struct methodtable *mtable;
    struct imethodtable *imtable;

    guess = ((unsigned int)index) >> 16;
    index &= 0xffff;

    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    imb = constant_pool[index].mb;
    intf = imb->fb.clazz;
    offset = imb->fb.u.offset;

    mtable = obj_methodtable(o);
    cb = mtable->classdescriptor;
    imtable = cbIntfMethodTable(cb);
    if (guess < (unsigned int)imtable->icount &&
	imtable->itable[guess].classdescriptor == intf)
	goto found_interface;
    for (guess = imtable->icount - 1; guess >= 0; guess--) {
	if (imtable->itable[guess].classdescriptor == intf) {
	    /* bury table index into push instruction */
#ifdef CODE_PATCH
	    unsigned char *caller_pc;

	    ASM("movl 0x4(%%ebp),%0" : "=r"(caller_pc));
	    /* back to push index inst */
	    caller_pc -= 10;
	    *(unsigned short *)(caller_pc + 3) = (unsigned short)guess;
#endif
	    goto found_interface;
	}
    }
    /* not found */
    { 
	char buf[256];
	char *p;

	jio_snprintf(buf, 256, 
		     "class %s does not implet interface %s",
		     cbName(cb), cbName(intf));
	for (p = buf; *p; p++) 
	    if (*p == '/') *p = '.';
	OpenJIT_Error(JAVAPKG "IncompatibleClassChangeError", buf);
    }

found_interface:
    mb = mt_slot(mtable, imtable->itable[guess].offsets[offset]);
    if (mb->CompiledCode == NULL) {
	char buf[256];

	if (mb->fb.name[0] == '+') {
	    jio_snprintf(buf, sizeof(buf), 
			 "access non-public method %s.%s through an interface",
			 cbName(mb->fb.clazz),
			 mb->fb.name + 1);
	    OpenJIT_Error(JAVAPKG "IllegalAccessError", buf);
	} else {
	    jio_snprintf(buf, sizeof(buf), 
			 "%s.%s",
			 cbName(mb->fb.clazz),
			 mb->fb.name);
	    OpenJIT_Error(JAVAPKG "AbstractMethodError", buf);
	}
    }
    *((struct methodblock * volatile *)&index) = mb;
    JUMP_IN_COMPILED_CODE(mb);
}

static void
OpenJIT_invokeinterface(int index, ...)
{
    struct methodblock	*caller_mb;
    cp_item_type	*constant_pool;

    GET_CALLER_MB(caller_mb);
    constant_pool = cbConstantPool(fieldclass(&caller_mb->fb));
    RESOLVE_CLASS_CONST(constant_pool, index, CONSTANT_InterfaceMethodref, caller_mb);
    PATCH_CODE_CALL(OpenJIT_invokeinterface_quick);
    ASM("leave; jmp OpenJIT_invokeinterface_quick");
}

static JHandle *
OpenJIT_newarray(int type, int size)
{
    JHandle *ret;

    if (size < 0) {
	OpenJIT_Error(JAVAPKG "NegativeArraySizeException", 0);
    }
    ret = ArrayAlloc(type, size);
    if (ret == 0) {
	OpenJIT_Error(JAVAPKG "OutOfMemoryError", 0);
    }
    return ret;
}

static void
OpenJIT_athrow(Hjava_lang_Object *obj)
{
    ExecEnv *ee = EE();
    struct methodblock *caller_mb;

    if (obj == NULL)
	OpenJIT_Error(JAVAPKG "NullPointerException", 0);
    GET_CALLER_MB(caller_mb);
    SET_COMPILED_FRAME(ee, caller_mb);
    exceptionThrow(ee, obj);
    handle_exception(ee);
}

#if defined(HAVE___MUL64)
extern int64_t __mul64(int64_t x, int64_t y);
#define OpenJIT_lmul	__mul64
#else
static int64_t OpenJIT_lmul(int64_t x, int64_t y)
{
    return x * y;
}
#endif

#if defined(HAVE___DIV64)
extern int64_t __div64(int64_t x, int64_t y);
#define OpenJIT_ldiv	__div64
#elif defined(HAVE___DIVDI3)
extern int64_t __divdi3(int64_t x, int64_t y);
#define OpenJIT_ldiv	__divdi3
#else
static int64_t OpenJIT_ldiv(int64_t x, int64_t y)
{
    return x / y;
}
#endif

#if defined(HAVE___rem64)
extern int64_t __rem64(int64_t x, int64_t y);
#define OpenJIT_lrem	__rem64
#elif defined(HAVE___MODDI3)
extern int64_t __moddi3(int64_t x, int64_t y);
#define OpenJIT_lrem	__moddi3
#else
static int64_t OpenJIT_lrem(int64_t x, int64_t y)
{
    return x % y;
}
#endif

extern int OpenJIT_f2i();	/* One arg is passed via floating ST(0) */
extern int OpenJIT_d2i();	/* One arg is passed via floating ST(0) */
extern int64_t OpenJIT_f2l(float f);
extern int64_t OpenJIT_d2l(double d);
__asm__("
OpenJIT_d2i = OpenJIT_f2i
.text
	.align 4
	.type	 OpenJIT_f2i,@function
OpenJIT_f2i:
	pushl %ebp
	movl %esp,%ebp
	subl $8,%esp
	ftst
	fnstcw -4(%ebp)
	movl -4(%ebp),%eax
	movb $0xc,%ah
	movw %ax,-2(%ebp)
	fldcw -2(%ebp)
	fistpl -8(%ebp)
	fldcw -4(%ebp)
	movl -8(%ebp),%eax
	cmp $0x80000000,%eax
	je IntOverFlow
	leave
	ret

IntOverFlow:
	fnstsw
	sahf
	jp	IntNaN
	movl	$0x80000000,%eax
	adcl	$-1,%eax
	leave
	ret
IntNaN:
	xorl	%eax,%eax
	leave
	ret

.text
	.align 4
	.type	 OpenJIT_f2l,@function
OpenJIT_f2l:
	pushl %ebp
	movl %esp,%ebp
	subl $12,%esp
	flds 8(%ebp)
	ftst
	fnstcw -4(%ebp)
	movl -4(%ebp),%eax
	movb $0xc,%ah
	movw %ax,-2(%ebp)
	fldcw -2(%ebp)
	fistpll -12(%ebp)
	fldcw -4(%ebp)
	movl -12(%ebp),%eax
	movl $0x80000000,%ecx
	movl -8(%ebp),%edx
	xorl %edx,%ecx
	orl %eax,%ecx
	je LongOverFlow
	leave
	ret

	.align 4
	.type	 OpenJIT_d2l,@function
OpenJIT_d2l:
	pushl %ebp
	movl %esp,%ebp
	subl $12,%esp
	fldl 8(%ebp)
	ftst
	fnstcw -4(%ebp)
	movl -4(%ebp),%eax
	movb $0xc,%ah
	movw %ax,-2(%ebp)
	fldcw -2(%ebp)
	fistpll -12(%ebp)
	fldcw -4(%ebp)
	movl -12(%ebp),%eax
	movl $0x80000000,%ecx
	movl -8(%ebp),%edx
	xorl %edx,%ecx
	orl %eax,%ecx
	je LongOverFlow
	leave
	ret

LongOverFlow:
	fnstsw
	sahf
	jp	LongNaN
	movl	$0,%eax
	adcl	$-1,%eax
	adcl	$-1,%edx
	leave
	ret

LongNaN:
	xorl	%eax,%eax
	xorl	%edx,%edx
	leave
	ret
");

static int64_t
OpenJIT_lshr(int64_t ll, unsigned b)
{
    return ll >> b;
}

static uint64_t
OpenJIT_lushr(uint64_t ll, unsigned b)
{
    return ll >> b;
}

static int64_t
OpenJIT_lshl(int64_t ll, unsigned b)
{
    return ll << b;
}

static float
OpenJIT_l2f(int64_t ll)
{
    return (float)ll;
}

static double
OpenJIT_l2d(int64_t ll)
{
    return (double)ll;
}

#define OpenJIT_drem	fmod

#if JDK_VERSION < 12
static void
OpenJIT_monitorEnter(unsigned key)
{
    if (key == 0)
	OpenJIT_Error(JAVAPKG "NullPointerException", 0);
    monitorEnter(key);
}

static void
OpenJIT_monitorExit(unsigned key)
{
    if (key == 0)
	OpenJIT_Error(JAVAPKG "NullPointerException", 0);
    monitorExit(key);
}
#else
static void
OpenJIT_monitorEnter(unsigned key)
{
    ExecEnv *ee;
    if (key == 0)
	OpenJIT_Error(JAVAPKG "NullPointerException", 0);
    ee = EE();
    monitorEnter2(ee, key);
    if (exceptionOccurred(ee))
	handle_exception(ee);
}

static void
OpenJIT_monitorExit(unsigned key)
{
    ExecEnv *ee;
    if (key == 0)
	OpenJIT_Error(JAVAPKG "NullPointerException", 0);
    ee = EE();
    monitorExit2(ee, key);
    if (exceptionOccurred(ee))
	handle_exception(ee);
}
#endif

/* exception and error handling */

static unsigned char *
find_handler(ExecEnv *ee, struct methodblock *mb,
		      JHandle *object, uint32_t *pc)
{
    int i;
    struct CatchFrame *cf;
    struct compiled_CatchFrame *ccf;
    cp_item_type *constant_pool = cbConstantPool(fieldclass(&mb->fb));

    for (i = 0; i < mb->exception_table_length; i++) { 
	cf = mb->exception_table + i;
	ccf = cf->compiled_CatchFrame;
	if (ccf->start_pc <= (caddr_t)pc && (caddr_t)pc <= ccf->end_pc) { 
	    /* Is the class name a superclass of object's class. */
	    if (cf->catchType == 0) {
		/* The finally case */
		return (unsigned char *)ccf->handler_pc;
	    } else { 
		/* The usual case */
		int catchType = cf->catchType;
		ClassClass *catchClass;
		unsigned char *type_table = constant_pool[CONSTANT_POOL_TYPE_TABLE_INDEX].type;
		JHandle *object = ee->exception.exc;

		exceptionClear(ee);
		if (!CONSTANT_POOL_TYPE_TABLE_IS_RESOLVED(type_table, catchType)) {
		    if (!ResolveClassConstantFromClass(fieldclass(&mb->fb), catchType, ee, 1 << CONSTANT_Class)) {
			return 0;
		    }
		}
		if (!exceptionOccurred(ee)) {
		    exceptionThrow(ee, object);
		}
		catchClass = constant_pool[catchType].clazz;
		if (is_instance_of(object, catchClass, ee)) {
		    return (unsigned char *)ccf->handler_pc;
		}
	    }
	}
    }
    /* not found */
    return 0;
}

static void
handle_exception (ExecEnv *ee)
{
    register uint32_t *ebp;
    uint32_t caller_pc;
    extern unsigned char skip_ex_beg[], skip_ex_end[];
    extern unsigned char invokeCC_beg[], invokeCC_end[];

    ASM ("movl %%ebp,%0" : "=r"(ebp) :);
    while(1) {
	caller_pc = ebp[1];

	/* discard stack frame of runtime routines */
	while (((unsigned char *)caller_pc >= skip_ex_beg) &&
	       ((unsigned char *)caller_pc < skip_ex_end)) {
	    ebp = (uint32_t *)(ebp[0]);
	    caller_pc = ebp[1];
	}
	if ((unsigned char *)caller_pc >= invokeCC_beg && 
	    (unsigned char *)caller_pc < invokeCC_end) {
	    ASM ("movl %0,%%ebp" : : "r"(ebp));
	    ASM ("leave");
	    ASM ("ret");
	}
	ebp = (uint32_t *)(ebp[0]);
	/* JVM on JDK1.1 references lastpc. ??? */
	ee->current_frame->lastpc = (unsigned char *)caller_pc;

	{
	    unsigned char *new_pc;
	    struct methodblock *mb;

	    mb = (struct methodblock *)(ebp[2]);
	    new_pc = find_handler(ee, mb, ee->exception.exc, (uint32_t *)caller_pc);
	    if (new_pc != 0) {	/* Found the exception handler. */
		exceptionClear(ee);
		/* save entry point in a register */
		ASM ("movl %0,%%ebx" : : "r" (new_pc) : "%ebx");
		/* calculate %esp. see genPrologue() in X86.java */
		ASM ("leal -0x4(%0,%1,4),%%esp" : : "r" (ebp), "r" (-(mb->nlocals + mb->maxstack)));
		/* optop = object. %eax is used to push Exception */
		ASM ("movl %0,%%eax" : : "m" (ee->exception.exc) : "%eax");
		/* set frame pointer(%ebp) */
		ASM ("movl %0,%%ebp" : : "r"(ebp));
		/* jump to catch frame */
		ASM ("jmp *%ebx");
	    }
	    /* not found in this stack frame */
	    if (mb->fb.access & ACC_SYNCHRONIZED) {
		if (mb->fb.access & ACC_STATIC) {
		    monitorExit(obj_monitor(cbHandle(fieldclass(&mb->fb))));
		} else {
		    monitorExit(obj_monitor(ebp[3]));
		}
	    }
	}
    }
}

void
OpenJIT_Error(char *ename, char *DetailMessage)
{
    ExecEnv *ee = EE();
    uint32_t *fp,*prev_fp;
    uint32_t caller_pc;
    extern unsigned char skip_err_beg[], skip_err_end[];

    ASM ("movl %%ebp,%0" : "=r"(fp):);
    do {
	caller_pc = (uint32_t)(fp[1]);
	prev_fp = fp;
	fp = (uint32_t *)(fp[0]);
    } while (((unsigned char *)caller_pc >= skip_err_beg) &&
	     ((unsigned char *)caller_pc < skip_err_end));
    ee->current_frame->current_method = (struct methodblock *)fp[2];
    ee->current_frame->vars = (stack_item *)prev_fp;
    SignalError(ee, ename, DetailMessage);
    handle_exception(ee);
    /* never returns here */
}

static void
catchZeroDivide()
{
    OpenJIT_Error(JAVAPKG "ArithmeticException", "/ by zero");
}

static void
catchNullPointer()
{
    OpenJIT_Error(JAVAPKG "NullPointerException", 0);
}

#if 0				/* not implement yet */
static void
catchStackOverflow()
{
    uint32_t *ebp, *rew_ebp;

    ASM ("movl %%ebp,%0" : "=r"(ebp):);
    /* rewind stack frame for processing SignalError */
    rew_ebp = ebp + 1024;
    while(ebp < rew_ebp) {
	ebp = (uint32_t *)(ebp[0]);
    }
    ASM ("movl %0,%%ebp" : : "r"(ebp));
    OpenJIT_Error(JAVAPKG "StackOverflowError", 0);
}
#endif

static void
catchArrayIndexOutOfBounds()
{
    OpenJIT_Error(JAVAPKG "ArrayIndexOutOfBoundsException", 0);
}

extern void throw_internal();
__asm__("
.text
	.align 4
	.type	 throw_internal,@function
throw_internal:
	pushl %eax
	jmp   *%edx
.Lfe_throw_internal:
	.size	 throw_internal,.Lfe_throw_internal-throw_internal
");

#if defined(__linux__)
static int signal_nest_count = 0;
#ifdef HAVE_ASM_UCONTEXT_H
static int signal_use_ucontext = 0;
#endif

#define EAX	eax
#define ECX	ecx
#define EDX	edx
#define EBX	ebx
#define ESP	esp
#define EBP	ebp
#define ESI	esi
#define EDI	edi
#define EIP	eip
#define SIGCONTEXT(M,R)	(M->R)
#define SIGRETURN(scp)				\
do {						\
    /* skip handler functions */		\
    ASM("movl %0, %%ebp" : : "r"(ebp));		\
    /* resume from signal */			\
    return;					\
} while(0);
#elif defined(__FreeBSD__)
#define EAX	sc_eax
#define ECX	sc_ecx
#define EDX	sc_edx
#define EBX	sc_ebx
#define ESP	sc_esp
#define EBP	sc_ebp
#define ESI	sc_esi
#define EDI	sc_edi
#define EIP	sc_eip
#define SIGCONTEXT(M,R)	(M->R)
#define SIGRETURN(scp)	sigreturn(scp)
#elif define(sun)
#define SIGCONTEXT(M,R)	M.gregs[REG_##R]
#define SIGRETURN(scp)	setcontext(scp)
#endif

#if JDK_VERSION < 12
#define SIGNAL_HANDLER_TYPE	void
#define SIGNAL_HANDLER_FAIL
#define SIGNAL_HANDLER_TRUE
#else  /* JDK1.2 */
#include <signal.h>
#define SIGNAL_HANDLER_TYPE	bool_t
#define SIGNAL_HANDLER_FAIL	FALSE
#define SIGNAL_HANDLER_TRUE	TRUE
#undef SIGRETURN(scp)
#define SIGRETURN(scp)	return TRUE
#endif

/* blackdown JDK(Linux) doesn't pass the "ucontext" infomation. :-<
 *
 * This function should be defined as:
 *
 *  OpenJIT_SignalHandler(int sig, struct siginfo *info, struct ucontext *uc)
 */

static SIGNAL_HANDLER_TYPE
OpenJIT_SignalHandler(int sig, void *info, void *uc)
{
    uint32_t *ebp;
    unsigned char *pc;
    struct sigcontext *scp;
    ExecEnv *ee = EE();

    /* OpenJIT doesn't care about native method */
    if ((ee->current_frame->current_method->fb.access & ACC_NATIVE) != 0)
	return SIGNAL_HANDLER_FAIL;

#if defined(__linux__)
    {
	int i;

	/* find out signal context information from stack */
	ASM("movl %%ebp,%0" : "=r"(ebp) :);
	for (i = 0; i < signal_nest_count; i++) {
	    /* walk up to caller function */
	    ebp = (uint32_t *)ebp[0];
	}
#ifdef HAVE_ASM_UCONTEXT_H
	if (signal_use_ucontext) {
	    struct ucontext *ucon = (struct ucontext *)ebp[4];
	    scp = &ucon->uc_mcontext;
	} else
#endif
	    scp = (struct sigcontext *)(ebp + 3);
    }
#elif defined(__FreeBSD__)
    scp = (struct sigcontext *)uc;
#elif defined(sun)
    scp = &((ucontext_t *)uc)->uc_mcontext;
#endif

    pc = (unsigned char *)SIGCONTEXT(scp, EIP);

    switch(sig) {
    case SIGFPE:
	/* 0x80000000 division by -1 causes overflow */
	/* JDK should handle this!!! :-< */
	if (SIGCONTEXT(scp, EDX) == 0xffffffff &&
	    SIGCONTEXT(scp, EAX) == 0x80000000) {
	    int divisor = 1;

	    if (pc[0] != 0xf7)	/* not idiv instruction */
		return SIGNAL_HANDLER_FAIL;
	    if ((pc[1] & 0xc0) == 0xc0) { /* divisor is on register */
		switch(pc[1] & 0x7) {
		// case 0: divisor = SIGCONTEXT(scp, EAX); break;
		case 1: divisor = SIGCONTEXT(scp, ECX); break;
		// case 2: divisor = SIGCONTEXT(scp, EDX); break;
		case 3: divisor = SIGCONTEXT(scp, EBX); break;
		// case 4: divisor = SIGCONTEXT(scp, ESP); break;
		// case 5: divisor = SIGCONTEXT(scp, EBP); break;
		case 6: divisor = SIGCONTEXT(scp, ESI); break;
		case 7: divisor = SIGCONTEXT(scp, EDI); break;
		default:
		    return SIGNAL_HANDLER_FAIL;
		}
	    } else if ((pc[1] & 0x38) != 0x38) { /* not idiv instruction */
		return SIGNAL_HANDLER_FAIL;
	    } else if ((pc[1] & 0x7) != 5) { /* base is not %ebp */
		return SIGNAL_HANDLER_FAIL;
	    } else if ((pc[1] & 0xc0) == 0x40) { /* offset: +d8 */
		/* divisor is on memory */
		divisor = (*(int32_t *)(SIGCONTEXT(scp, EBP) + (char)pc[2]));
	    } else if ((pc[1] & 0xc0) == 0x80) { /* offset: +d32 */
		/* divisor is on memory */
		divisor = (*(int32_t *)(SIGCONTEXT(scp, EBP) + *(int32_t *)(pc+2)));
	    } else {
		/* I don't care. OpenJIT doesn't generate such a code */
		return SIGNAL_HANDLER_FAIL;
	    }
	    if (divisor == -1) {
		/* ignore this signal */
		/* retry idiv instruction */
		SIGCONTEXT(scp, EDX) = 0x0;
		/* SIGCONTEXT(scp, EAX) = 0x80000000; */
		SIGRETURN(scp);
	    }
	    if (divisor != 0)
		return SIGNAL_HANDLER_FAIL;
	}
	SIGCONTEXT(scp, EAX) = (unsigned long)pc;
	SIGCONTEXT(scp, EDX) = (unsigned long)catchZeroDivide;
	SIGCONTEXT(scp, EIP) = (unsigned long)throw_internal;
	SIGRETURN(scp);

    case SIGSEGV:
#if defined(__FreeBSD__)
    case SIGBUS:
#endif
	if (*pc == 0xcd) {
	    if (pc[1] == 0) { /* long/0L */
		SIGCONTEXT(scp, EAX) = (unsigned long)pc;
		SIGCONTEXT(scp, EDX) = (unsigned long)catchZeroDivide;
		SIGCONTEXT(scp, EIP) = (unsigned long)throw_internal;
		SIGRETURN(scp);
	    } else {
		SIGCONTEXT(scp, EAX) = (unsigned long)pc;
		SIGCONTEXT(scp, EDX) = (unsigned long)catchArrayIndexOutOfBounds;
		SIGCONTEXT(scp, EIP) = (unsigned long)throw_internal;
		SIGRETURN(scp);
	    }
	}
	if (sig == SIGSEGV) {
	    SIGCONTEXT(scp, EAX) = (unsigned long)pc;
	    SIGCONTEXT(scp, EDX) = (unsigned long)catchNullPointer;
	    SIGCONTEXT(scp, EIP) = (unsigned long)throw_internal;
	    SIGRETURN(scp);
	}
    }
    return SIGNAL_HANDLER_FAIL;
}

/* end of function to skip stack frame for exceptions */
ASM_TEXT_LABEL(skip_ex_end);
ASM_TEXT_LABEL(skip_err_end);

#if JDK_VERSION >= 12
static void *
OpenJIT_CompiledFrameID(JavaFrame *frame)
{
    return (void *)frame->vars;
}
#endif

static JavaFrame *
OpenJIT_CompiledFramePrev(JavaFrame *frame, JavaFrame *buf)
{
    struct methodblock *mb;
    uint32_t *fp;
    unsigned char *caller_pc;
    extern unsigned char invokeCC_beg[], invokeCC_end[];

    /* OpenJIT should work outside of application environment. */
    /* So, cease traversing frame at the start of compile. */ 
    if (frame->current_method == OpenJITcompileMB) {
	return NULL;
    }
    if (frame->returnpc != COMPILED_FRAME_TAG) {
	/* this frame is created by JVM, not JIT */
	/* Maybe, it's created before compilation in multi-threaded */
        /* environment. */

	return frame->prev;
    }
    mb = frame->current_method;
    fp = (uint32_t *)(frame->vars);
    fp = (uint32_t *)fp[0];
    if (frame != buf) {
	/* first time */
	memset((char *)buf, 0, sizeof(JavaFrame));
	buf->returnpc = COMPILED_FRAME_TAG;
	buf->prev = frame->prev;
    }
    caller_pc = (unsigned char *)fp[1];
    if ((unsigned char *)caller_pc >= invokeCC_beg && 
	(unsigned char *)caller_pc < invokeCC_end) {
	return frame->prev;
    } else {
	buf->vars = (stack_item *)fp;
	fp = (uint32_t *)fp[0];
	buf->current_method = (struct methodblock *)fp[2];
	return buf;
    }
}

#if defined(__linux__)
static SIGNAL_HANDLER_TYPE
find_signal_context(int sig)
{
    uint32_t *ebp, *prev_ebp;
    struct sigcontext *scp;
    int nest;
    extern void exception_point();

    ASM("movl %%ebp,%0" : "=r"(ebp) :);
    for (nest = 0; nest < 10; nest++) {
	prev_ebp = (uint32_t *)ebp[0];

	scp = (struct sigcontext *)(ebp + 3);
	if (scp->eip == (unsigned long)exception_point &&
	    scp->ebp == (unsigned long)prev_ebp) {
	    signal_nest_count = nest;
	    scp->eip += 2;
	    SIGRETURN(scp);
	}

#ifdef HAVE_ASM_UCONTEXT_H
	{
	    struct ucontext *ucon;
	    /* ebp[0]: previous frame */
	    /* ebp[1]: previous eip */
	    /* ebp[2]: arg1(int signal) */
	    /* ebp[3]: arg2(siginfo_t *) ??? */
	    /* ebp[4]: arg3(struct ucontext *) ??? */
	    ucon = (struct ucontext *)ebp[4];
	    if ((ebp < (uint32_t *)ucon) && ((uint32_t *)ucon < prev_ebp)) {
		scp = &ucon->uc_mcontext;
		if (scp->eip == (unsigned long)exception_point &&
		    scp->ebp == (unsigned long)prev_ebp) {
		    signal_nest_count = nest;
		    signal_use_ucontext = 1;
		    scp->eip += 2;
		    SIGRETURN(scp);
		}
	    }
	}
#endif
	ebp = prev_ebp;
    }
    fprintf(stderr, "Warning!!! OpenJIT can't handle exceptions and errors correctly in this JVM.\n");
    return SIGNAL_HANDLER_TRUE;
}
#endif

#include "org_OpenJIT_Compile.h"
#include "org_OpenJIT_X86.h"

#define RT_INIT(FUNC, ATTR) \
    addr[org_OpenJIT_Compile_RT_##FUNC] = (int)OpenJIT_##FUNC; \
    attr[org_OpenJIT_Compile_RT_##FUNC] = ATTR;

#define VIRTBL	org_OpenJIT_X86_RT_ATTR_VIRTBL
#define PUSH32	org_OpenJIT_X86_RT_ATTR_PUSH32
#define STDCALL	org_OpenJIT_X86_RT_ATTR_STDCALL
#define REGPARM	org_OpenJIT_X86_RT_ATTR_REGPARM
#define VARARGC	org_OpenJIT_X86_RT_ATTR_VARARGC

void
OpenJIT_runtime_init(JITlink *linkage, int *addr, int *attr)
{
#if defined(__linux__)
    *linkage->p_CompiledCodeSignalHandler = (void *)find_signal_context;
    ASM("exception_point:int $0x10");
    if (signal_nest_count > 0)
	*linkage->p_CompiledCodeSignalHandler = (void *)OpenJIT_SignalHandler;
    else
	*linkage->p_CompiledCodeSignalHandler = NULL;
#else
    *linkage->p_CompiledCodeSignalHandler = (void *)OpenJIT_SignalHandler;
#endif
    *linkage->p_CompiledFramePrev = OpenJIT_CompiledFramePrev;

#if JDK_VERSION >= 12
    *linkage->p_CompiledFrameID = OpenJIT_CompiledFrameID;
    _linkclass_lock = *(linkage->linkclass_lock);
#endif
    classJavaLangObject = *(linkage->classJavaLangObject);

    RT_INIT(anewarray, STDCALL|PUSH32);
    RT_INIT(anewarray_quick, STDCALL);
    RT_INIT(athrow, STDCALL); /* not stdcall, but never returns */
    RT_INIT(check_aastore, STDCALL);
    RT_INIT(checkcast, STDCALL|PUSH32);
    RT_INIT(checkcast_quick, STDCALL);
    RT_INIT(d2i, 0);
    RT_INIT(d2l, 2);
    RT_INIT(dcmpg, 4);
    RT_INIT(dcmpl, 4);
    RT_INIT(drem, 4);
    RT_INIT(f2i, 0);
    RT_INIT(f2l, 1);
    RT_INIT(fcmpg, 2);
    RT_INIT(fcmpl, 2);
    RT_INIT(frem, 2);
    RT_INIT(getstatic, STDCALL|REGPARM);
    RT_INIT(instanceof, STDCALL|PUSH32);
    RT_INIT(instanceof_quick, STDCALL);
    RT_INIT(invokeinterface, PUSH32|VARARGC|1);
    RT_INIT(invokeinterface_quick, PUSH32|VARARGC|1);
    RT_INIT(invokespecial, VIRTBL|PUSH32|VARARGC|1);
    RT_INIT(invokespecial_resolve, VIRTBL|PUSH32|VARARGC|1);
    RT_INIT(invokesuper_quick, VIRTBL|PUSH32|VARARGC|1);
    RT_INIT(invokestatic, PUSH32|VARARGC|1);
    RT_INIT(invokestatic_resolve, PUSH32|VARARGC|1);
    RT_INIT(invokevirtual_resolve, VIRTBL|PUSH32|VARARGC|1);
    RT_INIT(l2d, 2);
    RT_INIT(l2f, 2);
    RT_INIT(lcmp, 4);
    RT_INIT(ldiv, 4);
    RT_INIT(lmul, 4);
    RT_INIT(lrem, 4);
    RT_INIT(lshl, 3);
    RT_INIT(lshr, 3);
    RT_INIT(lushr, 3);
    RT_INIT(monitorEnter, 1);
    RT_INIT(monitorExit, 1);
    RT_INIT(multianewarray, PUSH32|VARARGC|2);
    RT_INIT(multianewarray_quick, VARARGC|2);
    RT_INIT(newarray, STDCALL);
    RT_INIT(new, STDCALL|PUSH32);
    RT_INIT(new_quick, STDCALL);
    RT_INIT(resolveField, STDCALL|REGPARM);
    RT_INIT(resolveStaticField, STDCALL|REGPARM);
    RT_INIT(resolveString, STDCALL|REGPARM);
};
